﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace DnPakExplorer.Controls {

	/// <summary>
	/// 待实现功能
	/// 【锁定列】
	/// 【锁定行】
	/// 【编辑】
	/// 【多项选择】
	/// </summary>
	public partial class ListGridView : UserControl {

		private Color mBorderColor = Color.Gray;
		private Color mSelectedColor = Color.DodgerBlue;

		private int mScollX;	// X轴滚动大小，正表示内容左滚
		private int mScollY;	// Y轴滚动大小，正表示内容上滚

		private int mColumnHeaderHeight = 30;
		private int mRowHeaherWidth = 70;

		private MyVScrollBar mVScrollBar;
		private MyHScrollBar mHScrollBar;

		private StringFormat mTextFormat;
		private StringFormat mRowHeaderFormat;
		private StringFormat mColumnHeaderFormat;

		private int mRowTotalHeight;			// 所有行总高度
		private int mColumnTotalWidth;
		private int mFirstVisibleRowIndex;		// 第一个可见行的索引
		private int mFirstVisibleColumnIndex;
		private int mBaseVLine;				// 第一个可见行顶线到第一行顶线的距离
		private int mBaseHLine;

		private int mHitLineDelta = 2;
		private bool mCanDragGridLine = false;
		private bool mCanDragHeaderLine = true;
		private int mMinColumnWidth = 20;
		private int mMinRowHeight = 24;

		private List<ColumnHeader> mColumnHeaders = new List<ColumnHeader>();
		private List<RowHeader> mRowHeaders = new List<RowHeader>();

		private SolidBrush mTextBrush = (SolidBrush)Brushes.Black;
		private SolidBrush mBackgroundBrush = (SolidBrush)Brushes.White;
		private Pen mTmpPen = new Pen(Color.Gray);

		ListGridAdapter mAdapter;

		public OnGridHoverListener OnGridHover;

		public delegate void OnGridHoverListener(int row, int colum);

		#region Properties

		[Browsable(true)]
		public Color BorderColor {
			get { return mBorderColor; }
			set {
				mBorderColor = value;
				Invalidate();
			}
		}

		[Browsable(true)]
		public Color SelectedGridColor {
			get { return mSelectedColor; }
			set {
				mSelectedColor = value;
				Invalidate();
			}
		}

		[Browsable(true)]
		public bool CanDragGridLine {
			get { return mCanDragGridLine; }
			set {
				mCanDragGridLine = value;
			}
		}

		[Browsable(true)]
		public bool CanDragHeaderLine {
			get { return mCanDragHeaderLine; }
			set {
				mCanDragHeaderLine = true;
			}
		}

		[Browsable(true)]
		public int MinColumnWidth {
			get { return mMinColumnWidth; }
			set {
				mMinColumnWidth = Math.Max(0, value);
			}
		}

		[Browsable(true)]
		public int MinRowHeight {
			get { return mMinRowHeight; }
			set {
				mMinRowHeight = Math.Max(0, value);
			}
		}

		[Browsable(true)]
		public bool ClipGrid { get; set; }

		public StringFormat StringFormat {
			get { return mTextFormat; }
			set { mTextFormat = value; }
		}

		#endregion

		public ListGridView() {

			SetStyle(ControlStyles.OptimizedDoubleBuffer
				| ControlStyles.AllPaintingInWmPaint
				| ControlStyles.UserPaint, true);


			mVScrollBar = new MyVScrollBar();
			mHScrollBar = new MyHScrollBar();
			mVScrollBar.Dock = DockStyle.Right;
			mHScrollBar.Dock = DockStyle.Bottom;
			mVScrollBar.Minimum = 0;
			mHScrollBar.Minimum = 0;
			mVScrollBar.Scroll += OnScrollBarValueChanged;
			mVScrollBar.ValueChanged +=mVScrollBar_ValueChanged;
			mHScrollBar.Scroll += OnScrollBarValueChanged;
			mHScrollBar.ValueChanged +=mHScrollBar_ValueChanged;
			Controls.Add(mVScrollBar);
			Controls.Add(mHScrollBar);

			InitializeComponent();

			StringFormat = new StringFormat {
				Alignment = StringAlignment.Center,
				LineAlignment = StringAlignment.Center,
				Trimming = StringTrimming.EllipsisCharacter,
				FormatFlags = StringFormatFlags.LineLimit
			};

			mRowHeaderFormat = new StringFormat();
			mRowHeaderFormat.Alignment = StringAlignment.Center;
			mRowHeaderFormat.LineAlignment = StringAlignment.Center;
			mColumnHeaderFormat = new StringFormat();
			mColumnHeaderFormat.Alignment = StringAlignment.Center;
			mColumnHeaderFormat.LineAlignment = StringAlignment.Center;

			this.SizeChanged += OnSizeChanged;
			this.Paint += OnPaint;
			this.MouseWheel += OnMouseWheel;
			this.MouseMove += ListGridView_MouseMove;
			this.MouseDown += ListGridView_MouseDown;
			this.MouseUp += ListGridView_MouseUp;
			this.MouseLeave += ListGridView_MouseLeave;
			this.MouseCaptureChanged += ListGridView_MouseCaptureChanged;
			this.Load +=ListGridView_Load;
			if (GetService(typeof(System.ComponentModel.Design.IDesignerHost)) != null || System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime) {
				SetAdapter(new DesignTestAdapter());
				mSelectRect = new SelectRect(2, 3, 4, 4);
			}
		}


		void ListGridView_Load(object sender, EventArgs e) {
			//Controls.Remove(mVScrollBar);
			//Parent.Controls.Add(mVScrollBar);
		}

		void ListGridView_MouseCaptureChanged(object sender, EventArgs e) {
		}

		public void SetRowHeaderWidth(int width) {
			mRowHeaherWidth = width;
		}

		public void SetColumnHeaderHeight(int height) {
			mColumnHeaderHeight = height;
		}


		#region 动作和鼠标事件

		void ListGridView_MouseLeave(object sender, EventArgs e) {
			switch (mMouseAction) {
			case MouseAction.None:
				Cursor = Cursors.Arrow;
				break;
			}
		}

		void ListGridView_MouseUp(object sender, MouseEventArgs e) {
			if (e.Button == System.Windows.Forms.MouseButtons.Left) {
				switch (mMouseAction) {
				case MouseAction.DragColumnLine:
				case MouseAction.DragRowLine:
					Cursor = Cursors.Arrow;
					break;
				}
				mMouseAction = MouseAction.None;
				this.Capture = false;
			}
		}


		MouseAction mMouseAction;
		Point mStartIndex = new Point(-2, -2);
		Point mEndIndex = new Point(-2, -2);
		Point mDragLineMouseOrg;
		int mDragLineRawWidth;
		int mDragLineRawHeight;

		void ListGridView_MouseDown(object sender, MouseEventArgs e) {
			if (e.Button == System.Windows.Forms.MouseButtons.Left) {
				Point downIndex = mStartIndex = PointToCellIndexOfLine(e.Location);
				if (downIndex.X>=0 && downIndex.Y>=0) {
					// Rect Select
					mEndIndex = mStartIndex;
					mSelectRect.left = mSelectRect.right = mStartIndex.X;
					mSelectRect.top = mSelectRect.bottom = mStartIndex.Y;
					mMouseAction = MouseAction.RectSelect;
					this.Capture = true;
				} else if (downIndex.X == (int)IndexPosition.Header && downIndex.Y == (int)IndexPosition.Header) {
					// Select All
					mSelectRect = new SelectRect(0, 0, mColumnHeaders.Count-1, mRowHeaders.Count-1);
				} else if (downIndex.X == (int)IndexPosition.Header && downIndex.Y >= 0) {
					// Full Row Select
					mSelectRect.top = mSelectRect.bottom = mStartIndex.Y;
					mSelectRect.left = 0;
					mSelectRect.right = mColumnHeaders.Count;
					mMouseAction = MouseAction.FullRowSelect;
					this.Capture = true;
				} else if (downIndex.Y == (int)IndexPosition.Header && downIndex.X >= 0) {
					// Full Column Select
					mSelectRect.left = mSelectRect.right = mStartIndex.X;
					mSelectRect.top = 0;
					mSelectRect.bottom = mRowHeaders.Count;
					mMouseAction = MouseAction.FullColumnSelect;
					this.Capture = true;
				} else if (downIndex.Y == (int)IndexPosition.Line && downIndex.X>=-1) {
					// Drag Row Grid Line
					mMouseAction = MouseAction.DragColumnLine;
					mDragLineMouseOrg = e.Location;
					if (downIndex.X == -1)
						mDragLineRawWidth = mRowHeaherWidth;
					else
						mDragLineRawWidth = mColumnHeaders[mStartIndex.X].width;
					this.Capture = true;
				} else if (downIndex.X == (int)IndexPosition.Line && downIndex.Y>=-1) {
					// Drag Column Grid Line
					mMouseAction = MouseAction.DragRowLine;
					mDragLineMouseOrg = e.Location;
					if (downIndex.Y == -1)
						mDragLineRawHeight = mColumnHeaderHeight;
					else
						mDragLineRawHeight = mRowHeaders[mStartIndex.Y].height;
					this.Capture = true;
				}

				Repaint();
			}
		}

		void ListGridView_MouseMove(object sender, MouseEventArgs e) {
			switch (mMouseAction) {
			case MouseAction.RectSelect:
				Point p = PointToCellIndex(e.Location);
				if (p != mEndIndex) {
					mEndIndex = p;
					if (mEndIndex.X >= 0 && mEndIndex.Y>=0) {
						if (mStartIndex.X > mEndIndex.X) {
							mSelectRect.left = mEndIndex.X;
							mSelectRect.right = mStartIndex.X;
						} else {
							mSelectRect.left = mStartIndex.X;
							mSelectRect.right = mEndIndex.X;
						}
						if (mStartIndex.Y > mEndIndex.Y) {
							mSelectRect.top = mEndIndex.Y;
							mSelectRect.bottom = mStartIndex.Y;
						} else {
							mSelectRect.top = mStartIndex.Y;
							mSelectRect.bottom = mEndIndex.Y;
						}
						Repaint();
					}
				}
				break;
			case MouseAction.FullColumnSelect:
				p = PointToCellIndex(e.Location);
				if (p != mEndIndex) {
					mEndIndex = p;
					if (mEndIndex.X >= 0) {
						if (mStartIndex.X > mEndIndex.X) {
							mSelectRect.left = mEndIndex.X;
							mSelectRect.right = mStartIndex.X;
						} else {
							mSelectRect.left = mStartIndex.X;
							mSelectRect.right = mEndIndex.X;
						}
						Repaint();
					}
				}
				break;
			case MouseAction.FullRowSelect:
				p = PointToCellIndex(e.Location);
				if (p != mEndIndex) {
					mEndIndex = p;
					if (mEndIndex.Y>= 0) {
						if (mStartIndex.Y > mEndIndex.Y) {
							mSelectRect.top = mEndIndex.Y;
							mSelectRect.bottom = mStartIndex.Y;
						} else {
							mSelectRect.top = mStartIndex.Y;
							mSelectRect.bottom = mEndIndex.Y;
						}
					}
					Repaint();
				}
				break;
			case MouseAction.None:
				Point cellIndex = PointToCellIndexOfLine(e.Location);
				if (cellIndex.X == (int)IndexPosition.Header && cellIndex.Y >=0) {
					Cursor = Cursors.PanEast;
				} else if (cellIndex.Y == (int)IndexPosition.Header && cellIndex.X >=0) {
					Cursor = Cursors.PanSouth;
				} else if (cellIndex.X == (int)IndexPosition.Line && cellIndex.Y>=-1) {
					Cursor = Cursors.HSplit;
				} else if (cellIndex.Y == (int)IndexPosition.Line && cellIndex.X>=-1) {
					Cursor = Cursors.VSplit;
				} else {
					Cursor = Cursors.Arrow;
				}
				if (OnGridHover != null) {
					OnGridHover(cellIndex.Y, cellIndex.X);
				}
				break;

			case MouseAction.DragRowLine:
				if (mStartIndex.Y == (int)IndexPosition.Header) {
					mColumnHeaderHeight = Math.Max(mDragLineRawHeight + e.Location.Y - mDragLineMouseOrg.Y, MinRowHeight);
				} else {
					mRowHeaders[mStartIndex.Y].height = Math.Max(mDragLineRawHeight + e.Location.Y - mDragLineMouseOrg.Y, MinRowHeight);
				}
				CalculateRowTotalHeight();
				Repaint();
				break;

			case MouseAction.DragColumnLine:
				if (mStartIndex.X == (int)IndexPosition.Header) {
					mRowHeaherWidth = Math.Max(mDragLineRawWidth + e.Location.X - mDragLineMouseOrg.X, MinColumnWidth);
				} else {
					mColumnHeaders[mStartIndex.X].width = Math.Max(mDragLineRawWidth + e.Location.X - mDragLineMouseOrg.X, MinColumnWidth);
				}
				CalculateColumnTotalWidth();
				Repaint();
				break;
			}
		}

		#endregion

		Rectangle GridRegionRect {
			get {
				return new Rectangle(mRowHeaherWidth, mColumnHeaderHeight,
					ClientSize.Width-mRowHeaherWidth-(mVScrollBar.Visible?mVScrollBar.Width:0),
					ClientSize.Height-mColumnHeaderHeight-(mHScrollBar.Visible?mHScrollBar.Height:0));
			}
		}

		private int ListHeight {
			get { return ClientSize.Height-1-(mHScrollBar.Visible?mHScrollBar.Height:0); }
		}

		private int ListWidth {
			get { return ClientSize.Width-1-(mVScrollBar.Visible?mVScrollBar.Width:0); }
		}

		private Rectangle ListRect {
			get { return new Rectangle(0, 0, ListWidth, ListHeight); }
		}

		/// <summary>
		/// 设置数据适配器，不会刷新显示
		/// </summary>
		/// <param name="adapter"></param>
		public void SetAdapter(ListGridAdapter adapter) {
			Clear();
			mMouseAction = MouseAction.None;
			mAdapter = adapter;
			for (int i=0; i<mAdapter.RowCount; i++)
				mRowHeaders.Add(new RowHeader(Math.Max(mAdapter.GetRowHeight(i), MinRowHeight)));
			for (int i=0; i<mAdapter.ColumnCount; i++)
				mColumnHeaders.Add(new ColumnHeader(Math.Max(mAdapter.GetColumnWidth(i), MinColumnWidth)));

			CalculateRowTotalHeight();
			CalculateColumnTotalWidth();
			mScollX = 0;
			mScollY = 0;
			if (mVScrollBar.Value != 0)
				mVScrollBar.Value = 0;
			else
				CalculateVeticalScroll();
			if (mHScrollBar.Value != 0)
				mHScrollBar.Value = 0;
			else
				CalculateHorizontalScroll();
		}

		public void NotifyDataChanged() {
			if (mAdapter == null) {
				return;
			}
			mMouseAction = MouseAction.None;
			List<RowHeader> oldRow = mRowHeaders;
			List<ColumnHeader> oldColumn = mColumnHeaders;
			mRowHeaders = new List<RowHeader>();
			mColumnHeaders = new List<ColumnHeader>();
			for (int i=0; i<mAdapter.RowCount; i++) {
				if (oldRow.Count>i)
					mRowHeaders.Add(oldRow[i]);
				else
					mRowHeaders.Add(new RowHeader(Math.Max(mAdapter.GetRowHeight(i), MinRowHeight)));
			}
			for (int i=0; i<mAdapter.ColumnCount; i++) {
				if (oldColumn.Count>i)
					mColumnHeaders.Add(oldColumn[i]);
				else
					mColumnHeaders.Add(new ColumnHeader(Math.Max(mAdapter.GetColumnWidth(i), MinColumnWidth)));
			}
			CalculateRowTotalHeight();
			CalculateColumnTotalWidth();
			if (mRowTotalHeight-mScollY<GridRegionRect.Height) {
				mScollY = Math.Max(0, mRowTotalHeight-this.GridRegionRect.Height);
			}
			if (mColumnTotalWidth-mScollX<GridRegionRect.Width) {
				mScollX = Math.Max(0, mColumnTotalWidth-GridRegionRect.Width);
			}
			CalculateVeticalScroll();
			CalculateHorizontalScroll();
			Repaint();
		}

		public void SetRowHeight(int row, int height) {
			mRowHeaders[row].height = Math.Max(height, MinRowHeight);
		}

		public void SetColumnWidth(int column, int width) {
			mColumnHeaders[column].width = Math.Max(width, MinColumnWidth);
		}

		public void Clear() {
			mAdapter = null;
			mRowHeaders.Clear();
			mColumnHeaders.Clear();
		}

		public void BringToVisible(int row, int col) {
			int rowY = 0;
			int colX = 0;
			for (int i = 0; i<row; i++) {
				rowY += mRowHeaders[i].height;
			}
			for (int i = 0; i<col; i++) {
				colX += mColumnHeaders[i].width;
			}
			//mScollY = rowY;
			//mScollX = colX;
			//CalculateVeticalScroll();
			//CalculateHorizontalScroll();
			mHScrollBar.Value = Util.Clamp(colX, mHScrollBar.Minimum, Math.Max(mHScrollBar.Maximum-mHScrollBar.LargeChange, mHScrollBar.Minimum));
			mVScrollBar.Value = Util.Clamp(rowY, mVScrollBar.Minimum, Math.Max(mVScrollBar.Maximum-mVScrollBar.LargeChange, mVScrollBar.Minimum));
			//Repaint();
		}

		public void BringToVisible(int row) {
			int rowY = 0;
			for (int i = 0; i<row; i++) {
				rowY += mRowHeaders[i].height;
			}
			//mScollY = rowY;
			//CalculateVeticalScroll();
			mVScrollBar.Value = Util.Clamp(rowY, mVScrollBar.Minimum, Math.Max(mVScrollBar.Maximum-mVScrollBar.LargeChange, mVScrollBar.Minimum));
			//Repaint();
		}

		public void Select(int x, int y, int r, int b) {
			mSelectRect = new SelectRect(x, y, r, b);
			Repaint();
		}

		#region ScrollBars

		public void OnScrollBarValueChanged(object sender, ScrollEventArgs e) {
			if (e.ScrollOrientation == ScrollOrientation.VerticalScroll) {
				if (e.NewValue != e.OldValue) {
					mScollY = (int)(((double)mRowTotalHeight*e.NewValue/mVScrollBar.Maximum));
					CalculateVeticalScroll();
					Repaint();
				}
			} else {
				if (e.NewValue != e.OldValue) {
					mScollX = (int)(((double)mColumnTotalWidth*e.NewValue/mHScrollBar.Maximum));
					CalculateHorizontalScroll();
					Repaint();
				}
			}
		}

		void mHScrollBar_ValueChanged(object sender, EventArgs e) {
			mScollX = (int)(mHScrollBar.Value*((double)mColumnTotalWidth/mHScrollBar.Maximum));
			CalculateHorizontalScroll();
		}

		void mVScrollBar_ValueChanged(object sender, EventArgs e) {
			mScollY = (int)(mVScrollBar.Value*((double)mRowTotalHeight/mVScrollBar.Maximum));
			CalculateVeticalScroll();
		}

		void OnMouseWheel(object sender, MouseEventArgs e) {
			if (mHScrollBar.Bounds.Contains(e.Location))
				mHScrollBar.Value = Util.Clamp(mHScrollBar.Value-e.Delta, mHScrollBar.Minimum, Math.Max(mHScrollBar.Maximum-mHScrollBar.LargeChange, mHScrollBar.Minimum));
			else
				mVScrollBar.Value = Util.Clamp(mVScrollBar.Value-e.Delta, mVScrollBar.Minimum, Math.Max(mVScrollBar.Maximum-mVScrollBar.LargeChange, mVScrollBar.Minimum));
			Repaint();
		}

		#endregion

		#region Scroll Calculatings
		/// <summary>
		/// 根据当前的mScrollY值计算垂直滚动信息
		/// </summary>
		public void CalculateVeticalScroll() {
			mBaseVLine = 0;
			for (int i=0; i<mRowHeaders.Count; i++) {

				if (mScollY <= mBaseVLine + mRowHeaders[i].height) {
					mFirstVisibleRowIndex = i;
					break;
				}
				mBaseVLine += mRowHeaders[i].height;
			}
		}

		/// <summary>
		///  根据当前的mScrollX值计算水平滚动信息
		/// </summary>
		public void CalculateHorizontalScroll() {
			mBaseHLine = 0;
			for (int i=0; i<mColumnHeaders.Count; i++) {
				if (mScollX <= mBaseHLine + mColumnHeaders[i].width) {
					mFirstVisibleColumnIndex = i;
					break;
				}
				mBaseHLine += mColumnHeaders[i].width;
			}
		}

		public void CalculateRowTotalHeight() {
			mRowTotalHeight = 0;
			for (int i=0; i<mRowHeaders.Count; i++) {
				mRowTotalHeight += mRowHeaders[i].height;
			}
			mVScrollBar.Maximum = Math.Max(mRowTotalHeight, mVScrollBar.LargeChange);
			mVScrollBar.Visible = mRowTotalHeight > GridRegionRect.Height;
		}

		public void CalculateColumnTotalWidth() {
			mColumnTotalWidth = 0;
			for (int i=0; i<mColumnHeaders.Count; i++) {
				mColumnTotalWidth += mColumnHeaders[i].width;
			}
			mHScrollBar.Maximum = Math.Max(mColumnTotalWidth, mHScrollBar.LargeChange);
			mHScrollBar.Visible = mColumnTotalWidth > GridRegionRect.Width;

		}

		#endregion

		#region Coordinate Convert Functions

		public Point PointToCellIndex(Point position) {
			Point cellIndex = new Point((int)IndexPosition.WhiteSpace, (int)IndexPosition.WhiteSpace);

			// Right To Left
			if (position.X > this.Width-mVScrollBar.Width)
				cellIndex.X = (int)IndexPosition.OutofWindowHigh;
			else if (position.X > mRowHeaherWidth) {
				position.X = position.X - mRowHeaherWidth + mScollX;
				for (int col = mFirstVisibleColumnIndex, hLine = mBaseHLine; col<mColumnHeaders.Count; col++) {
					if ((hLine += mColumnHeaders[col].width) > position.X) {
						cellIndex.X = col;
						break;
					}
				}
			} else if (position.X > 0)
				cellIndex.X = (int)IndexPosition.Header;
			else
				cellIndex.X = (int)IndexPosition.OutofWindowLow;

			// Bottom To Top
			if (position.Y > this.Height-mHScrollBar.Height)
				cellIndex.Y = (int)IndexPosition.OutofWindowHigh;
			if (position.Y > mColumnHeaderHeight) {
				position.Y = position.Y - mColumnHeaderHeight + mScollY;
				for (int row = mFirstVisibleRowIndex, vLine = mBaseVLine; row<mRowHeaders.Count; row++) {
					if ((vLine += mRowHeaders[row].height) > position.Y) {
						cellIndex.Y = row;
						break;
					}
				}
			} else if (position.Y > 0)
				cellIndex.Y = (int)IndexPosition.Header;
			else
				cellIndex.Y = (int)IndexPosition.OutofWindowLow;
			return cellIndex;
		}

		public Point PointToCellIndexOfLine(Point position) {
			Point cellIndex = new Point((int)IndexPosition.WhiteSpace, (int)IndexPosition.WhiteSpace);

			// Right To Left
			if (position.X > this.Width-mVScrollBar.Width)
				cellIndex.X = (int)IndexPosition.OutofWindowHigh;
			else if (CanDragHeaderLine && Math.Abs(position.X - mRowHeaherWidth) <= mHitLineDelta) {
				cellIndex.X = (int)IndexPosition.Header;
				cellIndex.Y = (int)IndexPosition.Line;
				return cellIndex;
			} else if (position.X > mRowHeaherWidth) {
				bool canDragGridLine = CanDragGridLine;
				position.X = position.X - mRowHeaherWidth + mScollX;
				for (int col = mFirstVisibleColumnIndex, hLine = mBaseHLine; col<mColumnHeaders.Count; col++) {
					hLine += mColumnHeaders[col].width;
					if (canDragGridLine && Math.Abs(hLine - position.X) <= mHitLineDelta) {
						cellIndex.X = col;
						cellIndex.Y = (int)IndexPosition.Line;
						return cellIndex;
					} else if (hLine-position.X>0) {
						cellIndex.X = col;
						break;
					}
				}
			} else if (position.X > 0)
				cellIndex.X = (int)IndexPosition.Header;
			else
				cellIndex.X = (int)IndexPosition.OutofWindowLow;

			// Bottom To Top
			if (position.Y > this.Height-mHScrollBar.Height)
				cellIndex.Y = (int)IndexPosition.OutofWindowHigh;
			else if (CanDragHeaderLine && Math.Abs(position.Y - mColumnHeaderHeight) <= mHitLineDelta) {
				cellIndex.Y = (int)IndexPosition.Header;
				cellIndex.X = (int)IndexPosition.Line;
			} else if (position.Y > mColumnHeaderHeight) {
				bool canDragGridLine = CanDragGridLine;
				position.Y = position.Y - mColumnHeaderHeight + mScollY;
				for (int row = mFirstVisibleRowIndex, vLine = mBaseVLine; row<mRowHeaders.Count; row++) {
					vLine += mRowHeaders[row].height;
					if (canDragGridLine && Math.Abs(vLine-position.Y)<=mHitLineDelta) {
						cellIndex.Y = row;
						cellIndex.X = (int)IndexPosition.Line;
						return cellIndex;
					}
					if (vLine > position.Y) {
						cellIndex.Y = row;
						break;
					}
				}
			} else if (position.Y > 0)
				cellIndex.Y = (int)IndexPosition.Header;
			else
				cellIndex.Y = (int)IndexPosition.OutofWindowLow;
			return cellIndex;
		}

		#endregion

		public SelectRect GetSelected() {
			return new SelectRect(mSelectRect.left, mSelectRect.top, mSelectRect.right, mSelectRect.bottom);
		}

		SelectRect mSelectRect = new SelectRect(-2, -2, -2, -2);
		public bool IsGridSelected(int row, int col) {
			if (mSelectRect.left<-1 || mSelectRect.top<-1 || mSelectRect.right<-1 || mSelectRect.bottom<-1)
				return false;
			if (col>=mSelectRect.left && col<=mSelectRect.right && row>=mSelectRect.top && row<=mSelectRect.bottom)
				return true;
			return false;
		}

		#region Paintings

		public void OnPaint(object sender, PaintEventArgs e) {
			if (e.Graphics.VisibleClipBounds.IntersectsWith(new RectangleF(ListRect.X, ListRect.Y, ListRect.Width, ListRect.Height))) {
				Repaint(e.Graphics);
			}
		}

		protected void Repaint(Graphics g) {
			//g.Clear(BackColor);
			if (mAdapter != null) {
				mAdapter.BegainPaint(this);
				Region oldClip = g.Clip;
				bool transparent = BackColor.A!=255;
				Pen borderPen = new Pen(BorderColor);
				Rectangle contentRect = GridRegionRect;
				Point startPoint = new Point(contentRect.X-(mScollX-mBaseHLine), contentRect.Y - (mScollY-mBaseVLine));
				int rowCount = mRowHeaders.Count;
				int colCount = mColumnHeaders.Count;

				Grid gridTmp = new Grid();

				// 绘制行
				Rectangle gridRect = new Rectangle();
				Color backgroundColor = BackColor;
				Color textColor = ForeColor;

				for (int row=mFirstVisibleRowIndex, vLine = startPoint.Y; row<rowCount; row++) {
					gridRect.Y = vLine;
					gridRect.Height = mRowHeaders[row].height;

					for (int col=mFirstVisibleColumnIndex, hLine = startPoint.X; col<colCount; col++) {
						gridTmp.backgroudColor = backgroundColor;
						gridTmp.textColor = textColor;
						gridTmp.backgroudColor = IsGridSelected(row, col) ? SelectedGridColor : gridTmp.backgroudColor;
						mAdapter.GetGridData(row, col, gridTmp);

						gridRect.X = hLine;
						gridRect.Width = mColumnHeaders[col].width;

						if (transparent) {
							g.SetClip(contentRect);
						}

						mAdapter.OnDrawGridBackground(this, g, gridTmp.backgroudColor, row, col, gridRect);
						mAdapter.OnDrawGrid(this, g, gridTmp.text, gridTmp.textColor, row, col, gridRect);

						hLine += mColumnHeaders[col].width;
						if (hLine >= contentRect.Right) {
							break;
						}
					}

					// 绘制行标头
					if (transparent || ClipGrid) {
						g.SetClip(new Rectangle(0, mColumnHeaderHeight, mRowHeaherWidth+1, contentRect.Height+1));
					}
					gridTmp.backgroudColor = backgroundColor;
					gridTmp.textColor = textColor;
					mAdapter.GetRowHeader(row, gridTmp);
					mBackgroundBrush.Color = BackColor;
					mTextBrush.Color = gridTmp.textColor;

					gridRect.X = 0;
					gridRect.Width = mRowHeaherWidth;

					g.FillRectangle(mBackgroundBrush, gridRect);
					g.DrawRectangle(borderPen, gridRect.X, gridRect.Y, gridRect.Width, gridRect.Height);
					g.DrawString(gridTmp.text, Font, mTextBrush, gridRect, mRowHeaderFormat);

					vLine += mRowHeaders[row].height;
					if (vLine >= contentRect.Bottom) {
						break;
					}
				}

				if (transparent || ClipGrid) {
					g.SetClip(new Rectangle(mRowHeaherWidth, 0, contentRect.Width+1, mColumnHeaderHeight+1));
				}
				// 绘制列标头
				gridRect.Y = 0;
				gridRect.Height = mColumnHeaderHeight;
				for (int col=mFirstVisibleColumnIndex, hLine = startPoint.X; col<colCount; col++) {

					gridTmp.backgroudColor = backgroundColor;
					gridTmp.textColor = textColor;
					mAdapter.GetColumnHeader(col, gridTmp);
					mBackgroundBrush.Color = gridTmp.backgroudColor;
					mTextBrush.Color = gridTmp.textColor;

					gridRect.X = hLine;
					gridRect.Width = mColumnHeaders[col].width;

					g.FillRectangle(mBackgroundBrush, gridRect);
					g.DrawRectangle(borderPen, gridRect.X, gridRect.Y, gridRect.Width, gridRect.Height);
					g.DrawString(gridTmp.text, Font, mTextBrush, gridRect, mColumnHeaderFormat);

					hLine += mColumnHeaders[col].width;
					if (hLine >= contentRect.Right) {
						break;
					}
				}



				gridTmp.backgroudColor = backgroundColor;
				gridTmp.textColor = textColor;
				mAdapter.GetTopLeft(gridTmp);
				mBackgroundBrush.Color = gridTmp.backgroudColor;
				mTextBrush.Color = gridTmp.textColor;

				gridRect.Y = 0;
				gridRect.X = 0;
				gridRect.Height = mColumnHeaderHeight;
				gridRect.Width = mRowHeaherWidth;

				if (transparent || ClipGrid) {
					g.SetClip(new Rectangle(0, 0, mRowHeaherWidth+1, mRowHeaherWidth+1));
				}

				g.FillRectangle(mBackgroundBrush, gridRect);
				g.DrawRectangle(borderPen, gridRect.X, gridRect.Y, gridRect.Width, gridRect.Height);
				g.DrawString(gridTmp.text, Font, mTextBrush, gridRect, mRowHeaderFormat);

				g.Clip = oldClip;
				borderPen.Color = BorderColor;
				g.DrawRectangle(borderPen, ListRect);
				mAdapter.EndPaint(this);
			}
		}

		protected void Repaint() {
			Invalidate(false);
		}

		#endregion

		private void OnSizeChanged(object sender, EventArgs e) {
			mVScrollBar.LargeChange = Math.Max(GridRegionRect.Height, 0);
			mHScrollBar.LargeChange = Math.Max(GridRegionRect.Width, 0);
			mVScrollBar.Maximum = Math.Max(mRowTotalHeight, mVScrollBar.LargeChange);
			mHScrollBar.Maximum = Math.Max(mColumnTotalWidth, mHScrollBar.LargeChange);

			mHScrollBar.Visible = mColumnTotalWidth > GridRegionRect.Width;
			mVScrollBar.Visible = mRowTotalHeight > GridRegionRect.Height;

			int oldScrollY = mScollY;
			int oldScrollX = mScollX;
			if (mRowTotalHeight-mScollY<GridRegionRect.Height) {
				mScollY = Math.Max(0, mRowTotalHeight-this.GridRegionRect.Height);
			}
			if (mColumnTotalWidth-mScollX<GridRegionRect.Width) {
				mScollX = Math.Max(0, mColumnTotalWidth-GridRegionRect.Width);
			}
			if (oldScrollX != mScollX || oldScrollY != mScollY) {
				Invalidate(false);
			}
			CalculateVeticalScroll();
			CalculateHorizontalScroll();

		}


		class ColumnHeader {
			public int width;
			public ColumnHeader() {
				width = 100;
			}
			public ColumnHeader(int width) {
				this.width = width;
			}
		}

		class RowHeader {
			public int height;
			public RowHeader() {
				height = 24;
			}
			public RowHeader(int height) {
				this.height = height;
			}
		}

		public class SelectRect {
			public int left;
			public int top;
			public int right;
			public int bottom;

			public SelectRect(int l, int t, int r, int b) {
				left = l;
				top = t;
				right = r;
				bottom = b;
			}
		}

		enum MouseAction {
			RectSelect,
			FullRowSelect,
			FullColumnSelect,
			DragRowLine,
			DragColumnLine,
			None,
		}

		public enum IndexPosition : int {
			Header = -1,
			OutofWindowHigh = -4,
			OutofWindowLow = -3,
			Line = -5,
			WhiteSpace = -2,
		}

	}

	

	public class Grid{
		public Color backgroudColor = Color.WhiteSmoke;
		public Color textColor = Color.Black;
		public string text;
	}


	public abstract class ListGridAdapter {

		private SolidBrush mBackgroundBrush = new SolidBrush(Color.Empty);
		private Pen mBorderPen = new Pen(Color.Empty, 1);
		private SolidBrush mTextBrush = new SolidBrush(Color.Empty);

		public virtual void OnDrawGridBackground(ListGridView view, Graphics g, Color color, int row, int column, Rectangle rect) {
			mBackgroundBrush.Color = color;
			mBorderPen.Color = view.BorderColor;
			g.FillRectangle(mBackgroundBrush, rect);
			g.DrawRectangle(mBorderPen, rect);
		}

		public virtual void OnDrawGrid(ListGridView view, Graphics g, string text, Color color, int row, int column, Rectangle rect) {
			mTextBrush.Color = color;
			g.DrawString(text, view.Font, mTextBrush, rect, view.StringFormat);
		}

		public virtual void BegainPaint(ListGridView view) {

		}

		public virtual void EndPaint(ListGridView view) {

		}

		public virtual int GetRowHeight(int row){
			return 24;
		}

		public virtual int GetColumnWidth(int column) {
			return 100;
		}

		public abstract int RowCount {
			get;
		}

		public abstract int ColumnCount {
			get;
		}

		public abstract void GetTopLeft(Grid grid);

		public abstract void GetGridData(int row, int column, Grid grid);

		public abstract void GetRowHeader(int row, Grid grid);

		public abstract void GetColumnHeader(int column, Grid grid);

		public virtual string GetColumnHeaderTip(int column) {
			Grid grid = new Grid();
			GetColumnHeader(column, grid);
			return grid.text;
		}
	}

	public class DesignTestAdapter : ListGridAdapter {

		public DesignTestAdapter() {
		}

		public override int RowCount {
			get { return 1000; }
		}

		public override int ColumnCount {
			get { return 20; }
		}

		public override void GetGridData(int row, int column, Grid grid) {
			grid.text = row+","+column;
		}

		public override void GetRowHeader(int row, Grid grid) {
			grid.text = "Row"+row;
		}

		public override void GetColumnHeader(int column, Grid grid) {
			grid.text = "Column"+column;
		}

		public override void GetTopLeft(Grid drid) {
			drid.text = "_Key";
		}
	}
}
